/* eslint-disable max-statements */ import type { ParsedUrlQuery } from 'querystring'; import type { GetStaticPaths, GetStaticProps } from 'next'; import Head from 'next/head'; import { useRouter } from 'next/router'; import Script from 'next/script'; import { useCallback } from 'react'; import { useIntl } from 'react-intl'; import { getLayout, Heading, LinksWidget, PostsList, Pagination, type RenderPaginationLink, type RenderPaginationItemAriaLabel, Page, PageHeader, PageBody, PageSidebar, Spinner, Notice, LoadingPage, } from '../../../components'; import { convertWPThematicPreviewToPageLink, convertWPTopicPreviewToPageLink, fetchLastPostCursor, fetchPostsCount, fetchPostsList, fetchThematicsCount, fetchThematicsList, fetchTopicsCount, fetchTopicsList, } from '../../../services/graphql'; import styles from '../../../styles/pages/blog.module.scss'; import type { GraphQLConnection, Maybe, NextPageWithLayout, Nullable, WPPostPreview, WPThematicPreview, WPTopicPreview, } from '../../../types'; import { CONFIG } from '../../../utils/config'; import { ROUTES } from '../../../utils/constants'; import { getBlogSchema, getLinksItemData, getPostsWithUrl, getSchemaJson, getWebPageSchema, } from '../../../utils/helpers'; import { loadTranslation, type Messages } from '../../../utils/helpers/server'; import { useArticlesList, useBreadcrumb, useRedirection, useThematicsList, useTopicsList, } from '../../../utils/hooks'; const renderPaginationLink: RenderPaginationLink = (pageNum) => `${ROUTES.BLOG}/page/${pageNum}`; type BlogPageProps = { data: { posts: GraphQLConnection; thematics: GraphQLConnection; topics: GraphQLConnection; }; lastCursor: Maybe>; pageNumber: number; translation: Messages; }; /** * Blog index page. */ const BlogPage: NextPageWithLayout = ({ data, lastCursor, pageNumber, }) => { useRedirection({ isReplacing: true, to: ROUTES.BLOG, whenPathMatches: (path) => path === `${ROUTES.BLOG}/page/1`, }); const intl = useIntl(); const { isFallback } = useRouter(); const { articles, error, firstNewResultIndex, isLoading, isLoadingMore, isRefreshing, hasNextPage, loadMore, } = useArticlesList({ after: lastCursor, fallback: [data.posts], perPage: CONFIG.postsPerPage, }); const { isLoading: areThematicsLoading, thematics } = useThematicsList({ fallback: data.thematics, input: { first: data.thematics.pageInfo.total }, }); const { isLoading: areTopicsLoading, topics } = useTopicsList({ fallback: data.topics, input: { first: data.topics.pageInfo.total }, }); const messages = { loading: { thematicsList: intl.formatMessage({ defaultMessage: 'Thematics are loading...', description: 'BlogPage: loading thematics message', id: 'y37FuH', }), topicsList: intl.formatMessage({ defaultMessage: 'Topics are loading...', description: 'BlogPage: loading topics message', id: 'OsclKU', }), }, pageTitle: intl.formatMessage( { defaultMessage: 'Blog - Page {number}', description: 'BlogPage: page title with number', id: '8xVO3Y', }, { number: pageNumber, } ), pagination: { noJS: intl.formatMessage({ defaultMessage: "You can't load more articles without Javascript, please use the pagination instead.", description: 'BlogPage: pagination no script message', id: 'ZMES/E', }), title: intl.formatMessage({ defaultMessage: 'Pagination', description: 'BlogPage: pagination accessible name', id: 'AXe1Iz', }), }, seo: { metaDesc: intl.formatMessage( { defaultMessage: "Discover {websiteName}'s writings. He talks about web development, Linux and open source mostly.", description: 'BlogPage: SEO - Meta description', id: '18h/t0', }, { websiteName: CONFIG.name } ), title: intl.formatMessage( { defaultMessage: 'Blog: development, open source - Page {number} - {websiteName}', description: 'BlogPage: SEO - Page title', id: 'dG3sT3', }, { number: pageNumber, websiteName: CONFIG.name } ), }, widgets: { thematicsListTitle: intl.formatMessage({ defaultMessage: 'Thematics', description: 'BlogPage: thematics list widget title', id: 'HriY57', }), topicsListTitle: intl.formatMessage({ defaultMessage: 'Topics', description: 'BlogPage: topics list widget title', id: '2D9tB5', }), }, }; const { items: breadcrumbItems, schema: breadcrumbSchema } = useBreadcrumb({ title: messages.pageTitle, url: `${ROUTES.BLOG}/page/${pageNumber}`, }); const webpageSchema = getWebPageSchema({ description: messages.seo.metaDesc, locale: CONFIG.locales.defaultLocale, slug: ROUTES.BLOG, title: messages.pageTitle, }); const blogSchema = getBlogSchema({ isSinglePage: false, locale: CONFIG.locales.defaultLocale, slug: ROUTES.BLOG, }); const schemaJsonLd = getSchemaJson([webpageSchema, blogSchema]); const renderPaginationLabel: RenderPaginationItemAriaLabel = useCallback( ({ kind, pageNumber: number, isCurrentPage }) => { switch (kind) { case 'backward': return intl.formatMessage( { defaultMessage: 'Go to previous page, page {number}', description: 'BlogPage: previous page label', id: 'faO6BQ', }, { number } ); case 'forward': return intl.formatMessage( { defaultMessage: 'Go to next page, page {number}', description: 'BlogPage: next page label', id: 'oq3BzP', }, { number } ); case 'number': default: return isCurrentPage ? intl.formatMessage( { defaultMessage: 'Current page, page {number}', description: 'BlogPage: current page label', id: 'JL6G22', }, { number } ) : intl.formatMessage( { defaultMessage: 'Go to page {number}', description: 'BlogPage: page number label', id: 'IVczxR', }, { number } ); } }, [intl] ); if (isFallback) return ; const pageUrl = `${CONFIG.url}${ROUTES.BLOG}`; return ( {messages.seo.title} {/*eslint-disable-next-line react/jsx-no-literals -- Name allowed */} {/*eslint-disable-next-line react/jsx-no-literals -- Content allowed */}